package com.dily.study.demo.cas;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * Date: 2022-11-02 星期三
 * Time: 15:31
 * Author: Dily_Su
 * Remark:
 * - CAS是什么? CompareAndSet
 * - 多线程情况下比较并交换
 */
public class CASDemo {
    public static void main(String[] args) throws InterruptedException {
        CASDemo casDemo = new CASDemo();
//        casDemo.atomicStampedReferenceDemo();
        casDemo.loop();
    }


    /**
     * lambda 表达式中如下情况：
     * forEach 相当于调用匿名类内部方法，超出了 x 的作用域，
     * 则要通过将 x 变为原子变量实现直接操作内存来修改变量
     */
    private void lambdaAtomic() {
        AtomicInteger atomicInteger = new AtomicInteger(5);
        Integer x = 0;
        ArrayList<Integer> list = new ArrayList<>();
        list.forEach(e -> {
//             x += e;
            atomicInteger.addAndGet(e);
        });
    }

    /**
     * 基本数据类型的原子操作
     */
    private void atomicIntegerDemo() {
        AtomicInteger atomicInteger = new AtomicInteger(5);

        // main do somethings...

        System.out.println(atomicInteger.compareAndSet(5, 2019) + "\t current data：" + atomicInteger.get());

        System.out.println(atomicInteger.compareAndSet(5, 1024) + "\t current data：" + atomicInteger.get());
    }

    /**
     * 引用数据类型的原子操作
     * 有 ABA 问题
     */
    private void atomicReferenceDemo() {
        User z3 = new User("z3", 22);
        User l4 = new User("l4", 26);

        AtomicReference<User> reference = new AtomicReference<>();
        reference.set(z3);
        System.out.println(reference.compareAndSet(z3, l4) + "\t" + reference.get().toString());
        System.out.println(reference.compareAndSet(z3, l4) + "\t" + reference.get().toString());

    }

    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100, 1);

    /**
     * 引用数据类型的原子操作
     * ABA 问题 演示
     * 结果修改成功
     */
    private void atomicStampedReferenceDemo() {
        System.out.println("=========================ABA问题产生=======================");
        new Thread(() -> {
            atomicReference.compareAndSet(100, 101);
            atomicReference.compareAndSet(101, 100);
        }, "thread 1").start();

        new Thread(() -> {
            try { // 暂停一秒钟，保证 t1 完成ABA
                TimeUnit.SECONDS.sleep(1);
                System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "thread 2").start();

        System.out.println("=========================ABA问题解决=======================");
        new Thread(() -> {
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t第一次版本号" + stamp);
            try { // 暂停t3
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            stampedReference.compareAndSet(100, 101, stampedReference.getStamp(), stampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t第二次版本号" + stampedReference.getStamp());
            stampedReference.compareAndSet(101, 100, stampedReference.getStamp(), stampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t第三次版本号" + stampedReference.getStamp());
        }, "thread 3").start();

        new Thread(() -> {
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t第一次版本号" + stamp);
            try { // 暂停t4 3s 保证 t3 完成 ABA 操作
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean b = stampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "\t修改成功？" + b + "\t当前最新版本号" + stampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() + "\t当前实际最新值？" + stampedReference.getReference());
        }, "thread 4").start();
    }


    static AtomicInteger value = new AtomicInteger(0);

    public void loop() throws InterruptedException {
        new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                value.getAndIncrement();
            }
        }, "t1").start();
        new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                value.getAndIncrement();
            }
        }, "t2").start();
        TimeUnit.SECONDS.sleep(2);
        System.out.println(value.get());
    }

}

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
class User {
    String username;
    int age;
}
